package cn.seaboot.word.html;

import cn.seaboot.commons.core.CommonUtils;
import cn.seaboot.html.HtmlReader;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;

/**
 * 读取一个表格的内容
 * <p>
 * 表格会嵌套 body（段落或表格）
 *
 * @author Mr.css
 * @version 2025-06-26 15:13
 */
public class TableReader implements HtmlReader<XWPFTable> {

    /**
     * 结果写出
     */
    private final Writer writer;

    /**
     * body 元素读取
     */
    private HtmlReader<Iterator<IBodyElement>> bodyReader;

    public TableReader(Writer writer) {
        this.writer = writer;
    }

    public HtmlReader<Iterator<IBodyElement>> getBodyReader() {
        return bodyReader;
    }

    public void setBodyReader(HtmlReader<Iterator<IBodyElement>> bodyReader) {
        this.bodyReader = bodyReader;
    }

    /**
     * 读取表格内容
     *
     * @param table 表格对象
     * @throws IOException -
     */
    @Override
    public void read(XWPFTable table) throws IOException {
        List<XWPFTableRow> rows = table.getRows();

        if (CommonUtils.isEmpty(rows)) {
            writer.write("<table style=\"border:1px solid gray;\"><tbody></tbody></table>\r\n");
        } else {
            writer.write("<table style=\"border:1px solid gray;\"><tbody>\r\n");
            for (int i = 0; i < rows.size(); i++) {
                XWPFTableRow row = rows.get(i);
                List<XWPFTableCell> cells = row.getTableCells();

                if (CommonUtils.isEmpty(cells)) {
                    writer.write("<tr></tr>\r\n");
                    continue;
                }

                writer.write("<tr>\r\n");
                for (int j = 0; j < cells.size(); j++) {
                    XWPFTableCell cell = cells.get(j);
                    CTTc tc = cell.getCTTc();

                    // width：推荐保持单元格宽度，不然很容易错位
                    int width = cell.getWidth();

                    // style：工单类的文件，有时候会用表格布局，不可避免地需要隐藏部分边框
                    String className = border(cell);

                    // 单元格合并
                    CTVMerge vMerge = tc.getTcPr().getVMerge();
                    CTHMerge hMerge = tc.getTcPr().getHMerge();

                    // 普通单元格
                    if (vMerge == null && hMerge == null) {
                        writer.write("<td class=\"" + className + "\" style=\"width: " + width + "px;\">");
                        List<IBodyElement> list = cell.getBodyElements();
                        if (CommonUtils.isNotEmpty(list)) {
                            bodyReader.read(list.iterator());
                        }
                        writer.write("</td>\r\n");
                    } else {
                        // 水平合并单元格
                        int colspan = 1;
                        if (hMerge != null) {
                            if (hMerge.getVal() == STMerge.RESTART) {
                                colspan = colspan(j, row);
                            } else {
                                continue;
                            }
                        }

                        // 垂直合并单元格
                        int rowspan = 1;
                        if (vMerge != null) {
                            if (vMerge.getVal() == STMerge.RESTART) {
                                rowspan = rowspan(i, j, table);
                            } else {
                                continue;
                            }
                        }

                        writer.write("<td class=\"" + className + "\" style=\"width: " + width + "px;\" colspan=\"" + colspan + "\" rowspan=\"" + rowspan + "\">\r\n");
                        List<IBodyElement> list = cell.getBodyElements();
                        if (CommonUtils.isNotEmpty(list)) {
                            bodyReader.read(list.iterator());
                        }
                        writer.write("</td>\r\n");
                    }
                }
                writer.write("</tr>\r\n");

            }
            writer.write("</tbody></table>\r\n");
        }
    }

    /**
     * 获取单元格的边框
     * <p>
     * 这里获取的结果是对的，但是存在一个问题：
     * <p>
     * 以这样的表格为例：[a][b]，a 的右边框与 b 的左边框，是同一条。
     * 很明显，a 和 b 的样式，只会有一个生效。
     * 具体谁的样式生效，css 和 word 是不一样的，实际使用时，需要注意到这一点。
     *
     * @param cell 单元格
     * @return 单元格样式
     */
    private String border(XWPFTableCell cell) {
        CTTcBorders borders = cell.getCTTc().getTcPr().getTcBorders();
        if (borders == null) {
            return "";
        } else {
            StringBuilder sb = new StringBuilder();
            if (borders.getTop() != null) {
                sb.append("no-border-top ");
            }
            if (borders.getBottom() != null) {
                sb.append("no-border-bottom ");
            }
            if (borders.getLeft() != null) {
                sb.append("no-border-left ");
            }
            if (borders.getRight() != null) {
                sb.append("no-border-right ");
            }
            if (sb.length() > 0) {
                sb.setLength(sb.length() - 1);
            }
            return sb.toString();
        }
    }

    /**
     * 读取单元格垂直方向占用的行数。
     *
     * @param row   起始行号
     * @param col   所在列号
     * @param table 单元格所在的表格
     * @return 占用列数
     */
    private int rowspan(int row, int col, XWPFTable table) {
        int n = 1;
        List<XWPFTableRow> rows = table.getRows();
        for (int i = row + 1; i < rows.size(); i++) {
            XWPFTableCell cell = rows.get(i).getCell(col);
            CTVMerge merge = cell.getCTTc().getTcPr().getVMerge();
            if (merge == null || merge.getVal() != STMerge.CONTINUE) {
                return n;
            } else {
                n++;
            }
        }
        return n;
    }

    /**
     * 读取单元格水平方向占用的列数。
     *
     * @param col      起始列号
     * @param tableRow 单元格所在的行
     * @return 占用列数
     */
    private int colspan(int col, XWPFTableRow tableRow) {
        int n = 1;
        List<XWPFTableCell> cells = tableRow.getTableCells();
        for (int i = col + 1; i < cells.size(); i++) {
            XWPFTableCell cell = cells.get(i);
            CTHMerge merge = cell.getCTTc().getTcPr().getHMerge();
            if (merge == null || merge.getVal() != STMerge.CONTINUE) {
                return n;
            } else {
                n++;
            }
        }
        return n;
    }
}
